package org.simplextensions.registry;

import org.simplextensions.configuration.xml.ExtensionsConfiguration;
import org.simplextensions.configuration.xml.ExtensionsConfiguration.ScanPhaseListeners;
import org.simplextensions.configuration.xml.ScanPhaseListener;
import org.simplextensions.configuration.xml.ScanPhaseListenerType;
import org.simplextensions.scanner.ClassScannerBundle;
import org.simplextensions.scanner.IClassScanner;
import org.simplextensions.scanner.impl.ClassLoaderCSB;
import org.simplextensions.scanner.impl.DirCSB;
import org.simplextensions.scanner.impl.JarCSB;

import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.jar.JarFile;

public class SimpleXtensionsBundle {

	private final IExtensionRegistry extensionRegistry;

	private Map<String, Map<Integer, List<IClassScanner>>> scanListeners = new HashMap<String, Map<Integer, List<IClassScanner>>>();

	private final String name;

	private URL url;

	private final Set<String> classNames;

	public SimpleXtensionsBundle(IExtensionRegistry extensionRegistry, String name) {
		this(extensionRegistry, name, null, null);
	}

	public SimpleXtensionsBundle(IExtensionRegistry extensionRegistry, String name, Set<String> classNames, URL url) {
		this.extensionRegistry = extensionRegistry;
		this.name = name;
		this.classNames = classNames;
		this.url = url;
	}

	public SimpleXtensionsBundle(IExtensionRegistry extensionRegistry, ExtensionsConfiguration ec, URL url) throws InstantiationException,
			IllegalAccessException, ClassNotFoundException {
		this(extensionRegistry, ec.getName(), new HashSet<String>(ec.getClassNames().getClassName()), url);
		for (ScanPhaseListeners spl : ec.getScanPhaseListeners()) {
			for (ScanPhaseListener l : spl.getScanListener()) {
				ISimpleXtensionsClassScanner scanPhaseListener = (ISimpleXtensionsClassScanner) Class.forName(l.getClassName())
						.newInstance();
				int level = l.getType() == ScanPhaseListenerType.PRIMARY ? 1 : 2;
				addScanListener(l.getAnnotation(), scanPhaseListener, level);
			}
		}
	}

    public IExtensionRegistry getExtensionRegistry() {
        return extensionRegistry;
    }

    void addScanListener(Class<? extends Annotation> clazz, ISimpleXtensionsClassScanner scanPhaseListener, int level) {
		this.addScanListener(clazz.getCanonicalName(), scanPhaseListener, level);
	}

	void addScanListener(String annotationClassName, ISimpleXtensionsClassScanner scanPhaseListener, int level) {
		Map<Integer, List<IClassScanner>> map = this.scanListeners.get(annotationClassName);
		if (map == null) {
			this.scanListeners.put(annotationClassName, map = new HashMap<Integer, List<IClassScanner>>());
		}
		List<IClassScanner> list = map.get(level);
		if (list == null) {
			map.put(level, list = new LinkedList<IClassScanner>());
		}
		list.add(new ScanPhaseListenerAdapter(annotationClassName, extensionRegistry, this, scanPhaseListener));
	}

	public Set<String> getClassNames() {
		return classNames;
	}

	public String getName() {
		return name;
	}

	public URL getUrl() {
		return url;
	}

	public ClassScannerBundle getScannerBundle() {
		if (url != null) {
			String path = url.getPath();
			if (url.getProtocol().equals("file")) {
				return new DirCSB(getName(), new File(path).getParentFile(), scanListeners, classNames);
			} else if (url.getProtocol().equals("jar")) {
				try {
					String substring = path.substring(0, path.lastIndexOf("!"));
					
					return new JarCSB(getName(), new JarFile(new File(new URI(substring))), scanListeners,
							classNames);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return new ClassLoaderCSB(getName(), scanListeners, classNames);
	}

}
